package io.lvdaxian.mybatis17.scripting.xmltags;

import io.lvdaxian.mybatis17.builder.BaseBuilder;
import io.lvdaxian.mybatis17.mapping.SqlSource;
import io.lvdaxian.mybatis17.scripting.defaults.RawSqlSource;
import io.lvdaxian.mybatis17.scripting.xmltags.IfSqlNode;
import io.lvdaxian.mybatis17.scripting.xmltags.MixedSqlNode;
import io.lvdaxian.mybatis17.scripting.xmltags.TrimSqlNode;
import io.lvdaxian.mybatis17.session.Configuration;
import org.dom4j.Element;
import org.dom4j.Node;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * xml 脚本的构建器
 *
 * @author lihh
 */
public class XMLScriptBuilder extends BaseBuilder {
  
  private final Element element;
  private boolean isDynamic;
  private final Class<?> parameterType;
  private final Map<String, NodeHandler> nodeHandlerMap = new HashMap<>();
  
  public XMLScriptBuilder(Configuration configuration, Element element, Class<?> parameterType) {
    super(configuration);
    this.element = element;
    this.parameterType = parameterType;
    
    initNodeHandlerMap();
  }
  
  private void initNodeHandlerMap() {
    // 9种，实现其中2种 trim/where/set/foreach/if/choose/when/otherwise/bind
    // 这是一种策略模式的实现，用来处理不同的标签
    nodeHandlerMap.put("trim", new TrimHandler());
    nodeHandlerMap.put("if", new IfHandler());
  }
  
  /**
   * 解析 脚本节点
   *
   * @return sql源
   * @author lihh
   */
  public SqlSource parseScriptNode() {
    List<SqlNode> contents = parseDynamicTags(element);
    MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
    SqlSource sqlSource;
    // 是否动态标签
    if (isDynamic) {
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
  }
  
  /**
   * 解析 动态tag
   *
   * @param element el 元素
   * @return 返回 sqlNode 集合
   * @author lihh
   */
  public List<SqlNode> parseDynamicTags(Element element) {
    List<SqlNode> contents = new ArrayList<>();
    // 元素 集合 内容
    List<Node> children = element.content();
    
    // 遍历子节点
    for (Node child : children) {
      // 如果是 text 节点等
      if (child.getNodeType() == Node.TEXT_NODE || child.getNodeType() == Node.CDATA_SECTION_NODE) {
        // 节点 文本内容
        String data = child.getText();
        TextSqlNode textSqlNode = new TextSqlNode(data);
        if (textSqlNode.isDynamic()) {
          contents.add(textSqlNode);
          isDynamic = true;
        } else {
          contents.add(new StaticTextSqlNode(data));
        }
      } else if (child.getNodeType() == Node.ELEMENT_NODE) {
        String nodeName = child.getName();
        NodeHandler handler = nodeHandlerMap.get(nodeName);
        if (handler == null) {
          throw new RuntimeException("Unknown element <" + nodeName + "> in SQL statement.");
        }
        handler.handleNode(element.element(child.getName()), contents);
        isDynamic = true;
      }
    }
    return contents;
  }
  
  private interface NodeHandler {
    void handleNode(Element nodeToHandle, List<SqlNode> targetContents);
  }
  
  // trim handler 事件处理
  private class TrimHandler implements NodeHandler {
    
    /**
     * 节点元素 处理
     *
     * @param nodeToHandle   节点事件
     * @param targetContents sql content
     * @author lihh
     */
    @Override
    public void handleNode(Element nodeToHandle, List<SqlNode> targetContents) {
      // 动态 解析tag
      List<SqlNode> contents = parseDynamicTags(nodeToHandle);
      // 混合模式的 sql node
      MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
      // 前缀标签获取
      String prefix = nodeToHandle.attributeValue("prefix");
      // 覆盖 标签
      String prefixOverrides = nodeToHandle.attributeValue("prefixOverrides");
      // 后缀标签 获取
      String suffix = nodeToHandle.attributeValue("suffix");
      String suffixOverrides = nodeToHandle.attributeValue("suffixOverrides");
      TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
      targetContents.add(trim);
    }
  }
  
  // if handler 事件处理
  private class IfHandler implements NodeHandler {
    @Override
    public void handleNode(Element nodeToHandle, List<SqlNode> targetContents) {
      // 解析 动态 sql
      List<SqlNode> contents = parseDynamicTags(nodeToHandle);
      // 混合模式
      MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
      // 拿到 test 标签内容
      String test = nodeToHandle.attributeValue("test");
      // if sql node 节点
      IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
      targetContents.add(ifSqlNode);
    }
  }
}